package edu.xidian.sei.mlcs

import scala.collection.mutable.ListBuffer
import scala.io.Source
import scala.collection.mutable.ArrayBuffer
import scala.collection.mutable.Buffer
import scala.util.control.Breaks._

object MLCS {
  def buildStart(length: Int): Location = {
    Location(new Array[Short](length))
  }

  def buildEnd(seqs: Seq[Sequence]): Location = {
    val index = new Array[Short](seqs.length)
    var i = 0
    seqs foreach { seq =>
      index(i) = seq.length.toShort
      i += 1
    }
    new Location(index)
  }

  def loadFromFile(file: String): MLCS = {
    var seqs = ListBuffer[Sequence]()
    var charsets = Set[Char]()
    var strs = Source.fromFile(file).getLines()
    strs = strs.filter { p => p.length() > 0 }
    for (str <- strs) {
      val s = Sequence.build(str)
      charsets ++= str
      seqs += s
    }
    new MLCS(charsets, seqs.toList)
  }
}

case class Result(count: BigDecimal, length: Int)

final class MLCS(val charset: Set[Char], val seqs: List[Sequence]) {
  val start: Location = MLCS.buildStart(seqs.length)
  val end = MLCS.buildEnd(seqs)
  val charList: List[Char] = charset.toList
  // 分段搜索时设置的当前下标最大
  val currentLevel = seqs(0).length - 1
  // 多线程情况的最大线程数
  var maxThread = 0

  val successorTable = Array.ofDim[Short](charset.size, seqs.size, seqs(0).length)
  var i = 0
  charList.foreach { ch =>
    var j = 0
    seqs.foreach { seq =>
      successorTable(i)(j) = seq.buildSuccessors(ch)
      j += 1
    }
    i += 1
  }

  def charAt(location: Location): Char = seqs.head.charAt(location.index(0))

  def nextLocations(current: Location): ArrayBuffer[Location] = {
    val nexts = ArrayBuffer[Location]()
    for (k <- 0 until successorTable.length) {
      val snum = seqs.size
      val tmp = new Array[Short](seqs.size)
      breakable {
        for (j <- 0 until seqs.size) {
          val successor = successorTable(k)(j)(current.index(j))
          if (successor < 0 || successor > currentLevel) break
          else tmp(j) = successor
        }
      }

      if (tmp(tmp.length - 1) > 0) nexts += (new Location(tmp))
    }
    nexts
  }

  def nextPaths(current: Location): BoundedPaths = {
    val insides = new ArrayBuffer[Location]()
    val outsides = new ArrayBuffer[Location]()
    for (k <- 0 until successorTable.length) {
      val snum = seqs.size
      val tmp = new Array[Short](snum)
      var outofboundary = false
      breakable {
        for (m <- 0 until snum) {
          val successor = successorTable(k)(m)(current.index(m))
          if (successor < 0) break
          if (successor > currentLevel) outofboundary = true
          tmp(m) = successor
        }
      }
      if (tmp(tmp.length - 1) > 0) {
        if (outofboundary) outsides += (new Location(tmp))
        else insides += (new Location(tmp))
      }
    }
    new BoundedPaths(current, insides, outsides)
  }

  def nextSortedLocations(current: Location): Buffer[Location] = {
    nextLocations(current).sorted
  }

}